package org.dromara.system.controller;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.utils.BeanUtils;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.PayOrderStatusBo;
import org.dromara.system.domain.bo.cparams.OrderProduct;
import org.dromara.system.domain.bo.cparams.OrderUpdateBo;
import org.dromara.system.domain.mq.OrderCallbackParam;
import org.dromara.system.domain.vo.PayOrderAgentExploreVo;
import org.dromara.system.domain.vo.PayOrderClientExploreVo;
import org.dromara.system.domain.vo.PayOrderSupplierExploreVo;
import org.dromara.system.kernel.OrderChartKernel;
import org.dromara.system.kernel.OrderUpdateKernel;
import org.dromara.system.kernel.TrackPushKernel;
import org.dromara.system.mapper.PayGroupMapper;
import org.dromara.system.mapper.PayTypeMapper;
import org.dromara.system.service.IPayAppService;
import org.dromara.system.service.IPayOrderLogService;
import org.dromara.system.utils.PhpSerializeParser;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.system.domain.vo.PayOrderVo;
import org.dromara.system.domain.bo.PayOrderBo;
import org.dromara.system.service.IPayOrderService;
import org.dromara.common.mybatis.core.page.TableDataInfo;

/**
 * 订单
 * 前端访问路由地址为:/system/PayOrder
 *
 * @author Gavino
 * @date 2024-10-22
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/PayOrder")
public class PayOrderController extends BaseController {

    private final IPayOrderService payOrderService;
    private final PayTypeMapper payTypeMapper;
    private final PayGroupMapper payGroupMapper;
    private final OrderUpdateKernel updateKernel;
    private final IPayAppService payAppService;
    private final IPayOrderLogService orderLogService;
    private final TrackPushKernel trackPushKernel;
    private final OrderChartKernel orderChartKernel;

    /**
     * 查询订单列表
     */
    @SaCheckPermission("system:PayOrder:list")
    @GetMapping("/list")
    public TableDataInfo<PayOrderVo> list(PayOrderBo bo, PageQuery pageQuery) {
        TableDataInfo<PayOrderVo> dataInfo = payOrderService.queryPageList(bo, pageQuery);

        boolean isSuperAdmin = LoginHelper.isSuperAdmin();
        boolean isClient = LoginHelper.isClient();
        boolean isManager = LoginHelper.isManager();
        boolean isSupplier = LoginHelper.isSupplier();
        dataInfo.setRows(dataInfo.getRows().stream().peek(vo -> {
            if (Objects.equals(vo.getIsFeign(), 1)) {
                if (isClient || isManager) {
                    vo.setOrderCid(null);
                    vo.setPayStatus(3);
                }
                if (isSupplier) {
                    vo.setPayStatus(2);
                    vo.setStatus(2);
                }
                vo.setPayMethodType(null);
                vo.setRisk(null);
            }
            if (!isSuperAdmin) {
                vo.setOrderSn(null);
                vo.setHide(null);
            }
        }).toList());
        return dataInfo;
    }

    /**
     * 查询订单数量 金额
     */
    @SaCheckPermission("system:PayOrder:list")
    @GetMapping("/listCount")
    public R<?> listCount(PayOrderBo bo) {
        return R.ok(orderChartKernel.queryCount(bo));
    }

    /**
     * 导出订单列表
     */
    @SaCheckPermission("system:PayOrder:export")
    @Log(title = "订单", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(PayOrderBo bo, HttpServletResponse response) {

        boolean isClient = LoginHelper.isClient();
        boolean isSupplier = LoginHelper.isSupplier();

        Integer exportProduct = bo.getExportProduct();
        bo.setExportProduct(null);
        List<PayOrderAgentExploreVo> list = payOrderService.queryList(bo, PayOrderAgentExploreVo.class);

        //收款平台
        Set<Long> payTypeIds = list.stream().map(PayOrderAgentExploreVo::getPayTypeId).collect(Collectors.toSet());

        //查询结果,根据Id 转成map
        Map<Long, PayType> payTypes;
        if (!payTypeIds.isEmpty()) {
            //查询结果,根据Id 转成map
            payTypes = payTypeMapper.selectBatchIds(payTypeIds).stream().collect(Collectors.toMap(PayType::getTypeId, payType -> payType));
        } else {
            payTypes = new HashMap<>();
        }

        //轮询组
        Set<Long> groupIds = list.stream().map(PayOrderAgentExploreVo::getGroupId).collect(Collectors.toSet());
        Map<Long, PayGroup> groups;
        if (!isClient && !isSupplier && !groupIds.isEmpty()) {//不是商户，才查询轮询组
            //查询结果,根据Id 转成map
            groups = payGroupMapper.selectBatchIds(groupIds).stream().collect(Collectors.toMap(PayGroup::getGroupId, payGroup -> payGroup));
        } else {
            groups = new HashMap<>();
        }

        List<PayOrderAgentExploreVo> outs = new ArrayList<>();

        list.forEach(out -> {
            if (Objects.equals(out.getIsFeign(), 1)) {
                if (isClient) {
                    out.setOrderCid(null);
                    out.setPayStatus(3);
                }
                if (isSupplier) {
                    out.setPayStatus(2);
                    out.setStatus(2);
                }
            }

            PayType payType = payTypes.get(out.getPayTypeId());
            if (payType != null) {
                out.setPayTypeName(payType.getName());
            }

            PayGroup group = groups.get(out.getGroupId());
            if (group != null) {
                out.setGroupName(group.getGroupName());
            }

            if (StringUtils.isNotBlank(out.getCustomer())) {
                JSONObject json = JSONObject.parseObject(out.getCustomer());
                out.setFirstName(json.getString("first_name"));
                out.setLastName(json.getString("last_name"));
                out.setAddress_1(json.getString("address_1"));
                out.setAddress_2(json.getString("address_2"));
                out.setState(json.getString("state"));
                out.setCity(json.getString("city"));
                out.setPostcode(json.getString("postcode"));
                out.setCountry(json.getString("country"));
            }
            if (Objects.equals(exportProduct, 1) && StringUtils.isNotBlank(out.getProduct())) {
                List<OrderProduct> products = new ArrayList<>();
                try {
                    products = JSONArray.parseArray(out.getProduct(), OrderProduct.class);
                } catch (Exception ignore) {
                }
                if (!products.isEmpty()) {
                    int i = 0;
                    for (OrderProduct pro : products) {
                        if (i > 0) {
                            out = new PayOrderAgentExploreVo();
                        }
                        out.setName(pro.getName());
                        out.setImage(pro.getImage());

                        if (StringUtils.isNotBlank(pro.getAttributes())) {
                            String str = PhpSerializeParser.parse(pro.getAttributes());
                            if (StringUtils.isNotBlank(str)) {
                                out.setAttributes(str);
                            } else {
                                out.setAttributes(pro.getAttributes());
                            }
                        }
                        out.setQuantity(pro.getQuantity());
                        out.setPrice(pro.getPrice());

                        i++;
                        outs.add(out);
                    }
                } else {
                    outs.add(out);
                }
            } else {
                outs.add(out);
            }
        });

        //商户导出
        if (isClient) {
            ExcelUtil.exportExcel(BeanUtils.copyList(outs, PayOrderClientExploreVo.class), "订单", PayOrderClientExploreVo.class, response);
            return;
        }

        //供应商导出
        if (isSupplier) {
            ExcelUtil.exportExcel(BeanUtils.copyList(outs, PayOrderSupplierExploreVo.class), "订单", PayOrderSupplierExploreVo.class, response);
            return;
        }

        ExcelUtil.exportExcel(outs, "订单", PayOrderAgentExploreVo.class, response);
    }

    /**
     * 获取订单详细信息
     *
     * @param orderId 主键
     */
    @SaCheckPermission("system:PayOrder:query")
    @GetMapping("/{orderId}")
    public R<PayOrderVo> getInfo(@NotNull(message = "主键不能为空")
                                 @PathVariable Long orderId) {
        return R.ok(payOrderService.queryById(orderId));
    }

    /**
     * 新增订单
     */
    @SaCheckPermission("system:PayOrder:add")
    @Log(title = "订单", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<?> add(@Validated(AddGroup.class) @RequestBody PayOrderBo bo) {
        payOrderService.insertOrder(MapstructUtils.convert(bo, PayOrder.class));
        return R.ok();
    }

    /**
     * 修改订单
     */
    @SaCheckPermission("system:PayOrder:edit")
    @Log(title = "订单", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PayOrderBo bo) {
        return toAjax(payOrderService.updatePayOrder(MapstructUtils.convert(bo, PayOrder.class)));
    }

    /**
     * 修改订单
     */
    @RepeatSubmit()
    @PutMapping("/remark")
    public R<Void> remark(@RequestBody PayOrderBo bo) {
        if (bo.getOrderId() == null) {
            return R.fail("订单不存在");
        }

        PayOrder order = new PayOrder();
        order.setOrderId(bo.getOrderId());
        order.setRemark(bo.getRemark());
        return toAjax(payOrderService.updatePayOrder(order));
    }

    /**
     * 修改订单状态
     *
     * @param bo 订单
     * @return 结果
     */
    @SaCheckPermission("system:PayOrder:updateStatus")
    @RepeatSubmit()
    @Log(title = "修改订单状态", businessType = BusinessType.UPDATE)
    @PostMapping("/updateStatus")
    public R<?> updateStatus(@RequestBody PayOrderStatusBo bo) {

        PayOrder order = payOrderService.selectById(bo.getOrderId());
        if (order == null) {
            return R.fail("订单不存在");
        }

        //退款金额不能小于0，并且不能大于订单美元金额
        if (bo.getRefundMoney() != null && (bo.getRefundMoney().compareTo(BigDecimal.ZERO) <= 0 || bo.getRefundMoney().compareTo(order.getUsdMoney()) > 0)) {
            return R.fail("退款金额不能小于0，并且不能大于订单美元金额");
        }

        //更新订单
        long time = System.currentTimeMillis();
        PayOrder upObj = new PayOrder();
        upObj.setOrderId(order.getOrderId());

        String name = "";
        if (Objects.equals(bo.getType(), 1)) {
            name = "支付状态";
            upObj.setStatus(bo.getStatus());
            upObj.setPayStatus(bo.getStatus());
        } else if (Objects.equals(bo.getType(), 2)) {
            name = "退款状态";
            upObj.setRefundStatus(bo.getStatus());
        } else if (Objects.equals(bo.getType(), 3)) {
            name = "争议状态";
            upObj.setDisputeStatus(bo.getStatus());
            upObj.setDisputeRemark(bo.getDisputeRemark());
        }

        PayOrderLog log = orderLogService.saveLog(null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "手动更新订单【" + name + "】", JSONObject.toJSONString(bo), "success", System.currentTimeMillis() - time);

        //退款状态变更，是否通知平台退款
        if (Objects.equals(upObj.getRefundStatus(), 2) && bo.getPushRefund()) {
            //通知平台退款
            PayOrderLog log2 = orderLogService.saveLog(log.getLogId(), order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "通知平台退款", null, null, System.currentTimeMillis());
            R<OrderUpdateBo> r = updateKernel.handleRefund(order, log2, bo.getRefundMoney());
            if (R.isError(r)) {
                orderLogService.updateLog(log2.getLogId(), null, JSONObject.toJSONString(r, JSONWriter.Feature.WriteMapNullValue), System.currentTimeMillis() - log2.getTime());
                upObj.setRefundStatus(3);
            } else {
                OrderUpdateBo updateBo = r.getData();
                upObj.setRefundId(updateBo.getRefund_id());
            }
        }

        //退款金额
        if (Objects.equals(upObj.getRefundStatus(), 2)) {
            upObj.setRefundMoney(bo.getRefundMoney() != null ? bo.getRefundMoney() : order.getUsdMoney());
        } else {
            upObj.setRefundMoney(new BigDecimal(0));
        }

        if (Objects.equals(upObj.getStatus(), 2) || Objects.equals(upObj.getStatus(), 3)) {
            upObj.setPayTime(DateUtils.getNowDate());
        }

        payOrderService.updatePayOrder(upObj);

        JSONObject json = new JSONObject();
        if (bo.getPushA()) {
            //支付状态变更
            if (upObj.getStatus() != null) {
                json.put("pay_status", upObj.getStatus() == 1 ? "pending" : upObj.getStatus() == 2 ? "success" : "failed"); //A端订单状态
                json.put("fail_message", upObj.getStatus() == 3 ? "Manual confirmation of payment failed" : ""); //支付失败原因
            }

            //退款成功
            if (Objects.equals(upObj.getRefundStatus(), 2)) {
                json.put("pay_status", "refunded"); //退款成功
            }
            //退款未退款，并且支付状态是成功的
            if (Objects.equals(upObj.getRefundStatus(), 0) && order.getStatus() == 2) {
                json.put("pay_status", "success"); //支付成功
            }
        }

        //通知A端订单状态
        if (!json.isEmpty()) {
            json.put("order_id", order.getOrderAid()); //A端订单号
            json.put("pay_no", order.getOrderNo()); //B端单号
            if (!json.containsKey("fail_message")) {
                json.put("fail_message", "");
            }

            //应用账号配置
            PayApp app = payAppService.selectById(order.getAppId());
            //更新A端订单状态
            OrderCallbackParam param = new OrderCallbackParam();
            param.setOrderId(order.getOrderId());
            if (app != null) {
                param.setAppSecret(app.getAppSecret());
            }
            param.setLogId(log.getLogId());
            param.setJson(json);
            param.setCallNum(1);//次数
            updateKernel.handleOrderCallback(param);
        }

        return R.ok();
    }

    /**
     * 删除订单
     *
     * @param orderIds 主键串
     */
    @SaCheckPermission("system:PayOrder:remove")
    @Log(title = "订单", businessType = BusinessType.DELETE)
    @DeleteMapping("/{orderIds}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] orderIds) {
        return toAjax(payOrderService.deleteWithValidByIds(List.of(orderIds), true));
    }

    /**
     * 同步订单数据到ES
     *
     * @return
     */
    @SaCheckPermission("system:PayOrder:syncOrder")
    @PostMapping("/syncOrderToEs")
    public R<?> syncOrderToEs(@RequestParam(value = "orderId", required = false) Long orderId) {
        return payOrderService.syncOrderToEs(orderId);
    }

    /**
     * 物流推送
     *
     * @return 返回结果
     */
    @PostMapping("/trackPush")
    public R<?> trackPush(@RequestBody JSONObject json) {
        if (!json.containsKey("trackList")) {
            return R.fail("trackList参数不能为空");
        }
        List<JSONObject> list = json.getList("trackList", JSONObject.class);
        return trackPushKernel.push(list);
    }
}
